package cn.unknowpupil.init.controller.system;


import cn.unknowpupil.init.async.AsyncFactory;
import cn.unknowpupil.init.async.AsyncManager;
import cn.unknowpupil.init.common.enums.BusinessType;
import cn.unknowpupil.init.common.enums.LoginEnum;
import cn.unknowpupil.init.common.enums.ResultStatus;
import cn.unknowpupil.init.common.resopnse.ResultResponse;
import cn.unknowpupil.init.common.service.TokenService;
import cn.unknowpupil.init.components.annotation.Log;
import cn.unknowpupil.init.components.annotation.rest.AnonymousGetMapping;
import cn.unknowpupil.init.components.annotation.rest.AnonymousPostMapping;
import cn.unknowpupil.init.configure.oauth.UsernamePasswordLoginAuthentication;
import cn.unknowpupil.init.domain.dto.LoginUser;
import cn.unknowpupil.init.domain.dto.UsernamePasswordLoginDto;
import cn.unknowpupil.init.domain.system.SysUser;
import cn.unknowpupil.init.exceptions.HealthException;
import cn.unknowpupil.init.properties.RsaProperties;
import cn.unknowpupil.init.properties.SecurityProperties;
import cn.unknowpupil.init.service.system.ISysPermissionService;
import cn.unknowpupil.init.service.system.ISysRoleService;
import cn.unknowpupil.init.service.system.ISysUserService;
import cn.unknowpupil.init.utils.CaptchaUtils;
import cn.unknowpupil.init.utils.redis.RedisUtils;
import cn.unknowpupil.init.utils.security.RsaUtils;
import cn.unknowpupil.init.utils.security.SecurityUtils;
import cn.unknowpupil.init.utils.strings.StringUtils;
import com.google.common.collect.Maps;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;
import java.util.Set;


/**
 * Created at 2020/10/19 by w10g <br>
 * Initialize LoginController ... <br>
 *
 * @author unknowpupil
 * @since 0.0.1
 */
@Api(tags = {"系统-登录模块"}, value = "LoginController")
@RestController
@RequestMapping(value = "admin")
@RequiredArgsConstructor
@Slf4j
public class LoginController {
    private static final Logger logger = LoggerFactory.getLogger(LoginController.class);
    private final ISysUserService sysUserService;
    private final AsyncManager asyncManager;
    private final ISysRoleService sysRoleService;
    private final ISysPermissionService sysPermissionService;
    private final AuthenticationManagerBuilder authenticationManagerBuilder;
    private final CaptchaUtils captchaUtils;
    private final TokenService tokenService;
    private final RedisUtils redisUtils;
    private final SecurityProperties securityProperties;
    private final AuthenticationManager authenticationManager;

    @PostMapping("pass/login")
    public ResultResponse createAuthenticationToken(@RequestBody @Validated UsernamePasswordLoginDto dto) throws Exception {

        String code = (String) redisUtils.get(securityProperties.getCaptchaCodeKey() + dto.getUuid());

        if (StringUtils.isBlank(code)) {
            asyncManager.execute(AsyncFactory.recordLoginInfo(dto.getUsername(), LoginEnum.FAILURE, ResultStatus.CAPTCHA_EXIST_OR_EXPIRED.message()));

            throw new HealthException(ResultStatus.CAPTCHA_EXIST_OR_EXPIRED);
        }
        if (StringUtils.isBlank(dto.getCode()) || !dto.getCode().equalsIgnoreCase(code)) {
            asyncManager.execute(AsyncFactory.recordLoginInfo(dto.getUsername(), LoginEnum.FAILURE, ResultStatus.CAPTCHA_ERROR.message()));
            throw new HealthException(ResultStatus.CAPTCHA_ERROR);
        }
        // 清除验证码
        redisUtils.del(securityProperties.getCaptchaCodeKey() + dto.getUuid());

        log.info("username:{}, password:{}", dto.getUsername(), dto.getPassword());
        String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, dto.getPassword());

        Authentication authenticate = authenticate(dto.getUsername(), password);
        LoginUser userDetails = (LoginUser) authenticate.getPrincipal();
        String token = tokenService.createToken(userDetails);
        asyncManager.execute(AsyncFactory.updateUser(userDetails.getId()));
        logger.info("登录成功-------token: {}", token);
        return ResultResponse.buildSuccess(token);
    }

    /**
     * 登录验证
     *
     * @param dto 唯一标识
     * @return 结果
     */
    @ApiOperation("系统：登录验证")
    @AnonymousPostMapping("login")
    public ResultResponse login(@RequestBody @Validated UsernamePasswordLoginDto dto) throws Exception {
        // 这个步骤 和直接去校验 目前不清楚意义。
        // 密码解密
        String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, dto.getPassword());
        dto.setPassword(password);
        UsernamePasswordLoginAuthentication authenticationToken = new UsernamePasswordLoginAuthentication(dto, null);
        Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        LoginUser user = (LoginUser) authentication.getPrincipal();
        String token = tokenService.createToken(user);
        asyncManager.execute(AsyncFactory.updateUser(user.getId()));
        logger.info("登录成功-------token: {}", token);
        return ResultResponse.buildSuccess(token);

    }


    @ApiOperation("系统：获取用户详情")
    @GetMapping("info")
    public ResultResponse userInfo() {

        LoginUser loginUser = SecurityUtils.getLoginUser();
        SysUser user = sysUserService.info(loginUser.getId());

        Set<String> roles = sysRoleService.getRolePermission(user);

        Map<String, Object> result = Maps.newHashMap();
        Map<String, Object> menus = sysPermissionService.selectMenuTreeByUserId(loginUser);

        result.put("userName", user.getUserName());
        result.put("roles", roles);
        result.put("avatar", user.getAvatar());
        result.put("permissions", menus.get("permissions"));
        result.put("menus", menus.get("menus"));


        return ResultResponse.buildSuccess(result);
    }


    /**
     * 获取验证码
     */
    @ApiOperation("系统：退出登录")
    @Log(title = "退出登录", businessType = BusinessType.OTHER)
    @PostMapping(value = "logout", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResultResponse logout() {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        tokenService.logout(loginUser.getToken());
        asyncManager.execute(AsyncFactory.recordLoginInfo(loginUser.getUsername(), LoginEnum.LOGOUT, "退出成功"));
        return ResultResponse.success("退出成功");
    }

    @ApiOperation("系统：获取验证码")
    @AnonymousGetMapping("captcha")
    public ResultResponse captcha() {
        return ResultResponse.buildSuccess(captchaUtils.getCaptchaCode());
    }
    

    public Authentication authenticate(String username, String password) {
        try {
            return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
        } catch (DisabledException e) {
            throw new HealthException("账户不存在，请重试", e);
        } catch (BadCredentialsException e) {
            throw new HealthException("坏的凭证，请重试", e);
        }

    }
}
